package main

import (
	"fmt"
	"net"
	"time"
)

//Client -- 中转端
type Client struct {
	cons          map[int]net.Conn
	TargetAddress *net.TCPAddr
	ServerAddress *net.TCPAddr
}

func NewClient(targetAddress *net.TCPAddr, serverAddress *net.TCPAddr) *Client {
	return &Client{cons: make(map[int]net.Conn), TargetAddress: targetAddress, ServerAddress: serverAddress}
}

func (t *Client) Start() {
	//net.SplitHostPort("www.baidu.com:80")
	send, receive := createChannel(t.ServerAddress)

	for {
		select {
		case r := <-receive:
			cmd := r.Cmd()
			if cmd == CMDNEW {
				usrid := r.Usrid()
				fmt.Printf("创建目标(%v)连接.\n", usrid)
				_, exist := t.cons[usrid]
				if !exist {
					if c2, err := net.DialTCP("tcp", nil, t.TargetAddress); err == nil {
						t.cons[usrid] = c2
						go t.targetReceive(c2, usrid, send)
					} else {
						fmt.Println(err)
					}
				}
			} else if cmd == CMDPACKET {
				usrid := r.Usrid()
				if c, exist := t.cons[usrid]; exist {
					if _, err := c.Write(r.Payload()); err != nil {
						fmt.Println(err)
						cls := NewPacket(CMDCLOSE, uint32(usrid))
						send <- cls
						<-cls.done

						c.Close()
						delete(t.cons, usrid)
					}
				}
			} else if cmd == CMDCLOSE {
				usrid := r.Usrid()
				fmt.Printf("关闭目标(%v)\n", usrid)
				if c, exist := t.cons[usrid]; exist {
					delete(t.cons, usrid)
					c.Close()
				}
			} else {
				panic("未知的命令")
			}

			r.done <- true

		}
	}
}

func (t *Client) targetReceive(con net.Conn, conid int, send chan *packet) {
	p := NewPacket(CMDPACKET, uint32(conid))
	for {

		p.ResetPayload()
		Payload := p.Payload()
		tmp := Payload[:cap(Payload)]
		fmt.Print("*")
		n, err := con.Read(tmp)
		p.AllocPayload(uint32(n))

		if _, ok := t.cons[conid]; !ok {
			break
		}

		if n > 0 {
			send <- p
			<-p.done
		}

		if err != nil {
			fmt.Printf("目标连接关闭(%v).\n", conid)
			con.Close()
			delete(t.cons, conid)
			cls := NewPacket(CMDCLOSE, uint32(conid))
			send <- cls
			<-cls.done
			break
		}
	}
}

func createChannel(address *net.TCPAddr) (send, receive chan *packet) {
	fmt.Println("创建通道 >>>")

	send = make(chan *packet)
	receive = make(chan *packet)

	go func() {
		for {
			con, err := net.DialTCP("tcp", nil, address)
			if err != nil {
				panic(fmt.Sprintf("连接(%v)失败:%v", address, err))
			}

			stop := make(chan bool)
			go packetSend(con, send, stop)

			packetReceive(con, receive)

			close(stop)

			time.Sleep(time.Second)
		}
	}()
	return
}

func packetSend(con net.Conn, send chan *packet, stop chan bool) (err2 error) {
	defer func() {
		con.Close()
		if err := recover(); err != nil {
			fmt.Println(err)
			err2 = err.(error)
		}
	}()

	for {
		select {
		case p := <-send:
			_, err := con.Write(p.Bytes())
			p.done <- true

			if err != nil {
				panic(err)
			}
		case <-stop:
			return nil
		case <-time.After(time.Second * 30):
			hb := NewPacket(CMDHEARTBEAT, 0)
			if _, err := con.Write(hb.Bytes()); err != nil {
				panic(err)
			}
		}
	}
}

func packetReceive(con net.Conn, receive chan *packet) (err2 error) {
	defer func() {
		con.Close()
		if err := recover(); err != nil {
			fmt.Println(err)
			err2 = err.(error)
		}
	}()

	p := NewEmptyPacket()
	for {
		Depack(con, p)

		cmd := p.Cmd()
		if cmd == "HB" {
			//CMDHEARTBEAT
			fmt.Print(".")
		} else {
			receive <- p
			<-p.done
		}

		p.Reset()
	}
}
